

class TerrienData {
    public height: number
    public index: number
    public body: Box2D.Dynamics.b2Body
    public arch: Box2D.Common.Math.b2Vec2
    public Destroy(parent: RandomTerrien) {
        if (this.body != null) {
            parent.World.DestroyBody(this.body)
            this.body = null
        }
    }

}

class Terrien {
    public up: TerrienData
    public down: TerrienData
    public index: number

    public Destroy(parent: RandomTerrien) {
        if (this.up != null) {
            this.up.Destroy(parent)
            this.up = null
        }
        if (this.down != null) {
            this.down.Destroy(parent)
            this.down = null
        }
    }
}

class RandomTerrien {
    private _maxLength: number
    private _tall: number
    private _spaceRate: number
    private _cellWidth: number
    private _cellCount: number
    private _phyScene: PhyScene
    private _Player: Box2D.Dynamics.b2Body
    public get World(): Box2D.Dynamics.b2World {
        return this._phyScene.World
    }
    public get Player(): Box2D.Dynamics.b2Body {
        return this._Player
    }
    constructor(scene: PhyScene, player: Box2D.Dynamics.b2Body) {
        this._maxLength = 2 * App.StageUtils.getWidth()
        this._tall = App.StageUtils.getHeight()
        this._spaceRate = 0.3
        this._cellWidth = 60
        this._zoomHeight = 200
        this._cellCount = Math.ceil(this._maxLength / this._cellWidth / 2) * 2
        this._curIndex = -this._cellCount / 2
        this._phyScene = scene
        this._Player = player
        this._terrien = new Array<Terrien>(this._cellCount)
        console.log("RandomTerrien:  " + this._maxLength + "," + this._tall)
    }

    private _fromIndex: number = 0
    private _toIndex: number = 0
    private _terrien: Array<Terrien> = null
    private _inited: boolean = false
    private _realIndex: number = 0
    private _curIndex: number = 0
    private _zoomHeight: number
    public LookAt(x, y: number) {
        this.RandomData(x)
    }
    public RandomData(index: number) {
        this._realIndex = Math.ceil(index / this._cellWidth)
        index = this._realIndex + this._cellCount / 2
        if (this._inited && this._curIndex == index) {
            return
        }
        console.log("RandomData:" + index)

        for (let n = this._curIndex; n < index; n++) {
            let i = n % this._cellCount
            if (i < 0) {
                i = i + this._cellCount
            }
            if (this._terrien[i] == null) {
                this._terrien[i] = new Terrien()
            } else {
                this._terrien[i].Destroy(this)
            }
            this._terrien[i].index = n
            this._terrien[i].up = new TerrienData()
            this._terrien[i].up.index = n
            this._terrien[i].up.height = this._zoomHeight + Math.ceil(Math.random() * (this._tall * (1 - this._spaceRate))) / 2
            if (n < 10) {
                this._terrien[i].up.height = this._zoomHeight + this._tall / 6
            }

            this._terrien[i].down = new TerrienData()
            this._terrien[i].down.index = n
            this._terrien[i].down.height = this._zoomHeight + Math.ceil(Math.random() * (this._tall * (1 - this._spaceRate))) / 2
            if (n < 10) {
                this._terrien[i].down.height = this._zoomHeight + this._tall / 4
            }

            {
                let bodyDef = new Box2D.Dynamics.b2BodyDef()
                bodyDef.type = Box2D.Dynamics.b2Body.b2_staticBody
                bodyDef.bullet = true
                bodyDef.linearDamping = 0.1
                bodyDef.angularDamping = 0.2
                bodyDef.fixedRotation = false
                bodyDef.allowSleep = true
                bodyDef.position.x = this._phyScene.PhyLen(n * this._cellWidth)
                bodyDef.angle = Math.PI / 2

                let fixtureDef = new Box2D.Dynamics.b2FixtureDef()
                fixtureDef.density = 1
                fixtureDef.friction = 0
                fixtureDef.restitution = 0.8

                {
                    let t = this._terrien[i].up
                    let p = 1

                    bodyDef.position.y = p * this._phyScene.PhyLen(this._zoomHeight + this._tall / 2 - t.height / 2)
                    t.body = this._phyScene.World.CreateBody(bodyDef)
                    t.body.SetUserData(PhyObject.Create(PhyObjectType.Terrian))
                    {
                        let shape = new Box2D.Collision.Shapes.b2PolygonShape()
                        shape.SetAsBox(this._phyScene.PhyLen(t.height) / 2, this._phyScene.PhyLen(this._cellWidth) / 2)
                        fixtureDef.shape = shape
                        t.body.CreateFixture(fixtureDef)
                    }
                    {
                        let shape = new Box2D.Collision.Shapes.b2CircleShape()
                        shape.SetRadius(this._phyScene.PhyLen(5))
                        shape.SetLocalPosition((new Box2D.Common.Math.b2Vec2(-p * this._phyScene.PhyLen(t.height / 2 - 10), 0)))
                        fixtureDef.shape = shape
                        t.body.CreateFixture(fixtureDef)

                        t.arch = t.body.GetWorldPoint(shape.GetLocalPosition())
                    }
                }

                {
                    let t = this._terrien[i].down
                    let p = -1

                    bodyDef.position.y = p * this._phyScene.PhyLen(this._zoomHeight + this._tall / 2 - t.height / 2)
                    t.body = this._phyScene.World.CreateBody(bodyDef)
                    t.body.SetUserData(PhyObject.Create(PhyObjectType.Terrian))
                    {
                        let shape = new Box2D.Collision.Shapes.b2PolygonShape()
                        shape.SetAsBox(this._phyScene.PhyLen(t.height) / 2, this._phyScene.PhyLen(this._cellWidth) / 2)
                        fixtureDef.shape = shape
                        t.body.CreateFixture(fixtureDef)
                    }
                    {
                        let shape = new Box2D.Collision.Shapes.b2CircleShape()
                        shape.SetRadius(this._phyScene.PhyLen(5))
                        shape.SetLocalPosition((new Box2D.Common.Math.b2Vec2(-p * this._phyScene.PhyLen(t.height / 2 - 10), 0)))
                        fixtureDef.shape = shape
                        t.body.CreateFixture(fixtureDef)

                        t.arch = t.body.GetWorldPoint(shape.GetLocalPosition())
                    }
                }
            }
        }

        this._curIndex = index
        this._inited = true

        /*
        if (this._inited && index > this._fromIndex + this._maxLength / 4 && index < this._toIndex - this._maxLength / 4) {
            return
        }
        
        this._fromIndex = index - this._maxLength / 2
        this._toIndex = index + this._maxLength / 2
        */
    }

    public onTap() {
        let i = (this._realIndex + 2) % this._cellCount
        let t = this._terrien[i]
        console.log("Destroy:  " + t)
        //t.Destroy(this)
    }

    public _theJoint: Box2D.Dynamics.Joints.b2Joint
    public makeTheRole() {
        this.releaseTheRole()

        let i = (this._realIndex + 1) % this._cellCount
        let t = this._terrien[i]

        let joinDef = new Box2D.Dynamics.Joints.b2RevoluteJointDef()
        joinDef.Initialize(t.down.body, this.Player, t.down.arch)
        joinDef.enableMotor = true
        joinDef.enableLimit = false
        joinDef.collideConnected = false
        joinDef.motorSpeed = -180
        joinDef.maxMotorTorque = 1800
        this._theJoint = this.World.CreateJoint(joinDef)
    }

    public releaseTheRole() {
        if (this._theJoint != null) {
            this.World.DestroyJoint(this._theJoint)
        }
    }

}